package org.west.sky.scripture.exports.core;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.property.NumberFormatProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
public class ExportUtils {

    /**
     * 单sheet页写入Excel-合并策略
     *
     * @param clazz                导出实体类型
     * @param data                 导出数据集合
     * @param fileName             文件名
     * @param cellWriteHandlerList 合并策略集合
     */
    public static void writeExcelMerge(HttpServletResponse response, Class<?> clazz, List<?> data, String fileName, List<CellWriteHandler> cellWriteHandlerList) {
        export(response, fileName, data, clazz, null, null, cellWriteHandlerList, null);
    }

    /**
     * 单sheet页写入Excel-合并策略
     *
     * @param clazz    导出实体类型
     * @param data     导出数据集合
     * @param fileName 文件名
     */
    public static void export(HttpServletResponse response, String fileName, List<?> data, Class<?> clazz) {
        export(response, fileName, data, clazz, null);
    }

    /**
     * 下载Excel格式的数据
     *
     * @param response      response
     * @param fileName      文件名（支持中文）
     * @param data          待下载的数据
     * @param clazz         封装数据的POJO
     * @param excludeFields 无需导出的字段列表
     */
    public static void export(HttpServletResponse response, String fileName,
                              List<?> data, Class<?> clazz, List<String> excludeFields) {
        export(response, fileName, data, clazz, null, excludeFields, null, null);
    }

    /**
     * 下载Excel格式的数据
     *
     * @param response response
     * @param fileName 文件名（支持中文）
     * @param data     待下载的数据
     * @param head     表头
     */
    public static void export(HttpServletResponse response, String fileName, List<?> data, List<List<String>> head) {
        export(response, fileName, data, null, head, null, null, null);
    }

    /**
     * 允许表格样式定义（不完善，有需要自行扩展）
     *
     * @param response
     * @param fileName
     * @param data
     * @param headDTO
     */
    public static void customExport(HttpServletResponse response, String fileName, List<?> data, List<ExcelHeadDTO> headDTO) {
        List<List<String>> head = new ArrayList<>();
        Map<String, NumberFormatProperty> fieldNumberFormatProperty = new HashMap<>();
        for (ExcelHeadDTO dto : headDTO) {
            head.add(Collections.singletonList(dto.getHeadName()));
            if (ExcelHeadDTO.ExcelHeadStyle.NUMBER == dto.getStyle()) {
                String formatValve = "0" + (dto.getScale() < 0 ? "" : ("." + StrUtil.repeatByLength("0", dto.getScale())));
                NumberFormatProperty numberFormatProperty = new NumberFormatProperty(formatValve, RoundingMode.HALF_UP);
                fieldNumberFormatProperty.put(dto.getHeadName(), numberFormatProperty);
            }
        }
        export(response, fileName, data, null, head, null, null, fieldNumberFormatProperty);
    }

    /**
     * 下载Excel格式的数据
     *
     * @param response                  response
     * @param fileName                  文件名（支持中文）
     * @param data                      待下载的数据
     * @param clazz                     封装数据的POJO
     * @param cellWriteHandlerList      合并策略集合
     * @param fieldNumberFormatProperty 字段小数点格式化配置
     */
    public static void export(HttpServletResponse response, String fileName, List<?> data, Class<?> clazz, List<List<String>> head,
                              List<String> excludeFields, List<CellWriteHandler> cellWriteHandlerList,
                              Map<String, NumberFormatProperty> fieldNumberFormatProperty) {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码
            String encodedFileName = URLEncoder.encode(fileName, String.valueOf(StandardCharsets.UTF_8)).replaceAll("\\+", "%20");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, encodedFileName + ".xlsx");

            ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream(), clazz);
            if (null == clazz && !CollectionUtils.isEmpty(head)) {
                excelWriterBuilder.head(head);
                excelWriterBuilder.registerWriteHandler(getHeightAndFontStrategy());
                excelWriterBuilder.registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 20, (short) 18));
            }
            // 设置单元格宽度自适应
            excelWriterBuilder.registerWriteHandler(new FixedMatchColumnWidthStyleStrategy());
            //单元格行合并
            if (!CollectionUtils.isEmpty(cellWriteHandlerList)) {
                for (CellWriteHandler cellWriteHandler : cellWriteHandlerList) {
                    excelWriterBuilder.registerWriteHandler(cellWriteHandler);
                }
            }
            //不需要导出的单元格
            if (!CollectionUtils.isEmpty(excludeFields)) {
                // 假装可以重复设置
                excelWriterBuilder.excludeColumnFieldNames(excludeFields);
            }
            //sheet页数据
            List<? extends List<?>> partitioned = ListUtils.partition(data, 100_0000);
            if (partitioned.size() > 1) {
                try (ExcelWriter writer = excelWriterBuilder.build()) {
                    for (int i = 1; i <= partitioned.size(); i++) {
                        WriteSheet sheet = EasyExcel.writerSheet(fileName + "_" + i).build();
                        writer.write(partitioned.get(i - 1), sheet);
                        //                    excelWriterBuilder.sheet(fileName + "_" + i).doWrite(partitioned.get(i - 1));
                    }
                }
            } else {
                excelWriterBuilder.sheet(fileName).doWrite(data);
            }
        } catch (Exception e) {
            // 重置response
            log.error("文件下载失败" + e.getMessage());
            throw new RuntimeException("下载文件失败", e);
        }
    }

    /**
     * 填充导出
     *
     * @param response
     * @param fileName 导出文件名
     * @param templateName 模板文件名
     * @param rowMergeStrategy 合并策略
     * @param data 数据
     * @throws UnsupportedEncodingException
     */
    public static void fillExport(HttpServletResponse response, String fileName, String templateName, ExcelFillCellRowMergeStrategy rowMergeStrategy, Map<String, Object> data) throws UnsupportedEncodingException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        String encodedFileName = URLEncoder.encode(fileName, String.valueOf(StandardCharsets.UTF_8)).replaceAll("\\+", "%20");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, encodedFileName + ".xlsx");
        ClassPathResource resource = new ClassPathResource("excelTemplate/" + templateName);
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(resource.getInputStream()).inMemory(true).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet()
                    .registerWriteHandler(rowMergeStrategy)
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 20, (short) 18))
                    .build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();

            for (Map.Entry<String, Object> entry : data.entrySet()) {
                Object value = entry.getValue();
                FillWrapper dataWrapper;
                if (value instanceof List<?>) {
                    dataWrapper = new FillWrapper(entry.getKey(), (List<?>) value);
                } else {
                    dataWrapper = new FillWrapper(entry.getKey(), Collections.singletonList(value));
                }
                //填充数据
                excelWriter.fill(dataWrapper, fillConfig, writeSheet);
            }
        } catch (Exception e) {
            log.error("文件下载失败" + e.getMessage());
            throw new RuntimeException("下载文件失败", e);
        }

    }

    /**
     * 自定义Excel导出策略，设置表头和数据行的字体和高度
     *
     * @return Excel导出策略
     */
    private static HorizontalCellStyleStrategy getHeightAndFontStrategy() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //表头
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);

        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());

        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontHeightInPoints((short) 9);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

}
